//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>

#include "keyboard.h"
#include "vkey.h"
#include "i8042.h"
#include "lpc8712.h"
#include "ite8172.h"

//extern "C" void PrintComPort(const char *szMessage);

#ifdef _DEBUG
#define Assert(x)   if (!(x)) { \
                        kprintf("assert %s, %d\n", __FILE__, __LINE__); \
                        for(uint32_t jj = 0; jj < 1000*1000; jj++) { \
                            if (KbdHasData(0xb4000060)) { \
                                uint8_t uTemp; \
                                KRecvData(0xb4000060, &uTemp); \
                                kprintf("The data in keyboard" \
                                        "buffer = 0x%02x\n", uTemp); \
                            } \
                        } \
                        kprintf("Current keyboard status: 0x%02x\n", \
                                            i8042_READ_STATUS_PORT(0xb4000060)); \
                        DebugBreak(); \
                    }
#else
#define Assert(x)
#endif

#define SCANCODESET1

#ifdef SCANCODESET1

static uint8_t s_puKeymapSignal[] = {   // one byte scancode
    VK_ERROR, VK_ESCAPE, VK_1, VK_2, VK_3, VK_4, VK_5, VK_6, VK_7, VK_8,
    VK_9, VK_0, VK_HYPHEN, VK_EQUAL, VK_BACK, VK_TAB,  // 00 -- 0f
    VK_Q, VK_W, VK_E, VK_R, VK_T, VK_Y, VK_U, VK_I, VK_O, VK_P, VK_LBRACKET,
    VK_RBRACKET, VK_RETURN, VK_LCONTROL, VK_A, VK_S,        // 10 -- 1f
    VK_D, VK_F, VK_G, VK_H, VK_J, VK_K, VK_L, VK_SEMICOLON, VK_APOSTROPHE,
    VK_BACKQUOTE, VK_LSHIFT, VK_BACKSLASH, VK_Z, VK_X, VK_C, VK_V, // 20 -- 2f
    VK_B, VK_N, VK_M, VK_COMMA, VK_PERIOD, VK_SLASH, VK_RSHIFT, VK_MULTIPLY,
    VK_LMENU, VK_SPACE, VK_CAPITAL, VK_F1, VK_F2, VK_F3, VK_F4, VK_F5, //30-3f
    VK_F6, VK_F7, VK_F8, VK_F9, VK_F10, VK_NUMLOCK, VK_SCROLL, VK_NUMPAD7,
    VK_NUMPAD8, VK_NUMPAD9, VK_SUBTRACT, VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6,
    VK_ADD, VK_NUMPAD1,     // 40 -- 4f
    VK_NUMPAD2, VK_NUMPAD3, VK_NUMPAD0, VK_DECIMAL, VK_NOSUPPORT, VK_ERROR,
    VK_ERROR, VK_F11, VK_F12,   // 50 -- 58
};

static uint8_t s_puKeymapDouble[] = {  
    // double byte scancode, and first scancode is 0xE0
    VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR,
    VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR,
    VK_ERROR, VK_ERROR,     // 00 -- 0f
    VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR,
    VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_RETURN, VK_RCONTROL,
    VK_ERROR, VK_ERROR,     // 10 -- 1f
    VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR,
    VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR,
    VK_ERROR, VK_ERROR,     // 20 -- 2f
    VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_DIVIDE, VK_ERROR,
    VK_SNAPSHOT, VK_RMENU, VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR,
    VK_ERROR, VK_ERROR,     // 30 -- 3f
    VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_NOSUPPORT,
    VK_HOME, VK_UP, VK_PRIOR, VK_ERROR, VK_LEFT, VK_ERROR, VK_RIGHT,
    VK_ERROR, VK_END,     // 40 -- 4f
    VK_DOWN, VK_NEXT, VK_INSERT, VK_DELETE, VK_ERROR, VK_ERROR, VK_ERROR,
    VK_ERROR, VK_ERROR, VK_ERROR, VK_ERROR, VK_LWIN, VK_RWIN, VK_APPS,
    VK_Power, VK_Sleep,     // 50 -- 5f
};

static uint8_t s_puKeymapEspecial[] = {
    VK_HOME, VK_UP, VK_PRIOR, VK_SUBTRACT, VK_LEFT, VK_SPACE,
    VK_RIGHT, VK_ADD, VK_END,     // 47 -- 4f
    VK_DOWN, VK_NEXT, VK_INSERT, VK_DELETE,     // 50 -- 53
};

#endif

KBDInfo g_keyboardInfo = {0xb4000060 /* port */, 0x01 /* irq */};
#define INITVALUE           0xF1
#define COMMANDEXCEPTION    0x01
#define ARGUMENTEXCEPTION   0x02

static uint8_t s_uLastException = INITVALUE;
static uint8_t s_uPreScancode = INITVALUE;
static uint8_t s_uLastCommand = INITVALUE;
static uint8_t s_uLastArgument = INITVALUE;

IDeviceDriver *g_pKbdDriver = NULL;

ECode GetVirtualCode(KBDStatus *pVirtulCode)
{
    assert(NULL != g_pKbdDriver);

    MemoryBuf ezbbbVirtualCode((Byte *)pVirtulCode, sizeof(KBDStatus));
    return g_pKbdDriver->Read(0, 1, &ezbbbVirtualCode, NULL);
}

//
// The keyboard driver's classfactory
//
EXTERN IDeviceDriver * CDECL CreatePS2Keyboard(uint_t uDeviceNo, void *pvParameter)
{
    Keyboard *pKbdDriver;
    ECode ec;

    InitLPCInterface();

    if (SearchIT8712()) {
        kprintf("Found IT8712 Super IO\n");

        pKbdDriver = new Keyboard;
        if (NULL == pKbdDriver) {
            kprintf("There have no enough memory, %s, %d\n",
                                                        __FILE__, __LINE__);
            return NULL;
        }

        // Initialize keyboard hardware
        ec = pKbdDriver->Initialize(&g_keyboardInfo);
        if (FAILED(ec)) {
            kprintf("Unable to initialize keyboard\n");
            LPCSetConfig(LDN_KEYBOARD, 0x30, 0x0);      // disable keyboard
            delete pKbdDriver;
            return NULL;
        }

        //
        // enable keyboard
        //
        LPCSetConfig(LDN_KEYBOARD, (char)0x30, (char)0x1);

        //
        // enable change 0x71 register
        //
        LPCSetConfig(LDN_KEYBOARD, (char)0xf0, (char)0x0);

        //
        // active high, edeg trigger
        //
        LPCSetConfig(LDN_KEYBOARD, (char)0x71, (char)0x2);

        //
        // enable mouse
        //
//        LPCSetConfig(LDN_MOUSE, (char)0x30, (char)0x1);

        //
        // enable EC
        //
        LPCSetConfig(LDN_EC, (char)0x30, (char)0x1);

        //
        // enalbe keyboard event
        //
        LPCSetConfig(LDN_EC, (char)0xf0, (char)0x8);
        // disable auto-swp, see 8712 LPC document
        LPCSetConfig(LDN_EC, (char)0xf4,
                    (char)LPCGetConfig(LDN_EC, char(0xf4)|0x80));

        if (LPCGetConfig(LDN_KEYBOARD, 0x30) == 0) {
            kprintf("Error: keyboard not enabled\n");
        }

        kprintf("initialize keyboard OK\n");
    }
    else {
        // Search it8712F LPC chip
        kprintf("IT8712 Super IO not found\n");
        return NULL;
    }

    g_pKbdDriver = pKbdDriver;
    pKbdDriver->AddRef();

    return pKbdDriver;
}

//
// ISR
//
void CDECL KeyboardIsr(
    irq_t irq, void *pvDevice, InterruptContext *pContext)
{
    uint8_t c;
    Keyboard *pKbd = (Keyboard *)pvDevice;

    Assert(pKbd);

    c = i8042_READ_DATA_PORT(pKbd->m_uPortBase);

    // When you press multikey at the same time, the keyboard will send nothing
    // but 0xff
    if (0xFF == c) {
        return;
    }
    // Sometime, you send the command and press key all together, the command's
    // respond will delay, It's not failed, so you must continue deal with the
    // command in the isr. This case be looked as a exception manage.
    else if (0xFA == c) {
//        Assert(INITVALUE != s_uLastException);
        if (INITVALUE == s_uLastException) {
//            while (KbdHasData(pKbd->m_uPortBase)) {
//                uint8_t uTemp;
//                KRecvData(pKbd->m_uPortBase, &uTemp);
//            }
            return;
        }

        // The command succeeded.
        if (COMMANDEXCEPTION == s_uLastException) {
            kprintf("Send commnad 0x%02x succeeded\n", s_uLastCommand);

            // Send the argument be not success.
            s_uLastException = ARGUMENTEXCEPTION;

            Assert(INITVALUE != s_uLastArgument);

            // Send the command's argument.
            if (KbdSendData(pKbd->m_uPortBase, s_uLastArgument)) {
                return;
            }
        }
        else {
            // Send the argument succeeded.
            Assert(ARGUMENTEXCEPTION == s_uLastException);
        }

        // The succeeded command is Set Leds, so you must set the m_uLedSatus
        // in the keyboard struct.
        if (KBD_CMD_SET_LEDS == s_uLastCommand) {
            uint8_t uTempStatus = ((uint8_t)(pKbd->m_uLedStatus & 0x0f))
                                        ^ ((uint8_t)(s_uLastArgument & 0x0f));

            // Set the scancode, Let send virtual code go on.
            switch (uTempStatus) {
                case 0x01:{
                    c = 0x46;
                    break;
                }
                case 0x02:{
                    c = 0x45;
                    break;
                }
                case 0x04:{
                    c = 0x3A;
                    break;
                }
                default:{
                    kprintf("uTempStatus = 0x%02x\n", uTempStatus);
                    Assert(0);
                }
            }

            pKbd->m_uLedStatus = s_uLastArgument;
        }
        else if (KBD_CMD_SET_RATE == s_uLastCommand) {
            pKbd->m_uTypematic = s_uLastArgument;
            s_uLastException = INITVALUE;
            s_uLastCommand = INITVALUE;
            s_uLastArgument = INITVALUE;

            return;
        }
        else {
            Assert(0);
        }

        s_uLastException = INITVALUE;
        s_uLastCommand = INITVALUE;
        s_uLastArgument = INITVALUE;
    }
    // Send the last command or argument failed.
    else if (0xFE == c) {
        kprintf("The last Command and argument is 0x%02x, 0x%02x\n"
                    ">>>>> The Status = 0x%02x >>>>>>>>>> \n"
                    "exception type = 0x%02x\n",
                                    s_uLastCommand, s_uLastArgument,
                                    i8042_READ_STATUS_PORT(pKbd->m_uPortBase),
                                    s_uLastException);
    }
    else if (pKbd->SetLedStatus(c)) {
        // Set the led be no succeess.
        return;
    }

    if (!pKbd->TranslateScancodeToVirtualKeycode(c)) {
        pKbd->m_event.NotifyByIsr(TRUE);
    }
}

uint32_t Keyboard::SetLedStatus(uint8_t c)
{
    switch (c) {
        case 0x3A:{ // Caps Lock press
            if (!(m_uLedStatus & KBD_CAPSLOCK_HOLD)) {
                Assert(INITVALUE == s_uLastCommand);
                Assert(INITVALUE == s_uLastArgument);
                Assert(INITVALUE == s_uLastException);

                s_uLastCommand = KBD_CMD_SET_LEDS;
                s_uLastArgument = (m_uLedStatus ^ 0x4) | KBD_CAPSLOCK_HOLD;
                s_uLastException = COMMANDEXCEPTION;
                if (KbdSendData(m_uPortBase, s_uLastCommand)) {
                    return 1;
                }

                s_uLastException = ARGUMENTEXCEPTION;
                if (KbdSendData(m_uPortBase, s_uLastArgument)) {
                    return 1;
                }

                m_uLedStatus = s_uLastArgument;
                s_uLastCommand = INITVALUE;
                s_uLastArgument = INITVALUE;
                s_uLastException = INITVALUE;
            }

            break;
        }

        case 0xBA:{ // Caps Lock release
            m_uLedStatus &= ~KBD_CAPSLOCK_HOLD;

            break;
        }

        case 0x45:{ // Num Lock press
            if (0xE1 != s_uPreScancode
                && (!(m_uLedStatus & KBD_NUMLOCK_HOLD))) {
                Assert(INITVALUE == s_uLastCommand);
                Assert(INITVALUE == s_uLastArgument);
                Assert(INITVALUE == s_uLastException);

                s_uLastCommand = KBD_CMD_SET_LEDS;
                s_uLastArgument = (m_uLedStatus ^ 0x2) | KBD_NUMLOCK_HOLD;
                s_uLastException = COMMANDEXCEPTION;

                if (KbdSendData(m_uPortBase, s_uLastCommand)) {
                    return 1;
                }

                s_uLastException = ARGUMENTEXCEPTION;
                if (KbdSendData(m_uPortBase, s_uLastArgument)) {

                    return 1;
                }

                m_uLedStatus = s_uLastArgument;
                s_uLastCommand = INITVALUE;
                s_uLastArgument = INITVALUE;
                s_uLastException = INITVALUE;
            }

            break;
        }

        case 0xC5:{ // Num Lock release
            if (0xE1 != s_uPreScancode) {
                m_uLedStatus &= ~KBD_NUMLOCK_HOLD;
            }

            break;
        }

        case 0x46:{ // Scroll Lock press
            if (!(m_uLedStatus & KBD_SCROLLLOCK_HOLD)) {
                Assert(INITVALUE == s_uLastCommand);
                Assert(INITVALUE == s_uLastArgument);
                Assert(INITVALUE == s_uLastException);

                s_uLastCommand = KBD_CMD_SET_LEDS;
                s_uLastArgument = (m_uLedStatus ^ 0x1) | KBD_SCROLLLOCK_HOLD;
                s_uLastException = COMMANDEXCEPTION;

                if (KbdSendData(m_uPortBase, s_uLastCommand)) {
                    return 1;
                }

                s_uLastException = ARGUMENTEXCEPTION;
                if (KbdSendData(m_uPortBase, s_uLastArgument)) {
                    return 1;
                }

                m_uLedStatus = s_uLastArgument;
                s_uLastCommand = INITVALUE;
                s_uLastArgument = INITVALUE;
                s_uLastException = INITVALUE;
            }

            break;
        }

        case 0xC6:{ // Scroll Lock release
            m_uLedStatus &= ~KBD_SCROLLLOCK_HOLD;

            break;
        }
    }

    return 0;
}

uint32_t Keyboard::TranslateScancodeToVirtualKeycode(uint8_t c)
{
    // The first byte of multibyte scancode.
    if (0xE1 == c || 0xE0 == c) {
        s_uPreScancode = c;
        return 1;
    }

    // mark the keyboard, becase the system maybe have several keyboard.
    m_virtualKeyCode[m_uTailer].uKeyboardIdentifier = 0;

    // Process the PAUSE key, It's especially.
    if (0xE1 == s_uPreScancode) {
        if (0xC5 == c) {
            m_virtualKeyCode[m_uTailer].bKeyPress = TRUE;
            m_virtualKeyCode[m_uTailer].uVirKey = VK_PAUSE;
        }
        else {
            return 1;
        }
    }
    else if (0xE0 == s_uPreScancode) {
        // Set the key status, press or release
        m_virtualKeyCode[m_uTailer].bKeyPress = ((c & 0x80) == 0x00);

        // Ignore the scancode 0xE0 0x2A and 0xE0 0xAA
        if (0x2A == c || 0xAA == c) {
            s_uPreScancode = INITVALUE;
            return 1;
        }
        else if (0x63 == c || 0xE3 == c) {
            m_virtualKeyCode[m_uTailer].uVirKey = VK_WakeUp;
        }
        else {
            c &= 0x7F;
            m_virtualKeyCode[m_uTailer].uVirKey = s_puKeymapDouble[c];

            if (c > 0x5F) {
                m_virtualKeyCode[m_uTailer].uVirKey = VK_NOSUPPORT;
            }

            // process Right CTRL and Right ALT
            switch (c) {
                case 0x1D:{ // Right CTRL
                    if (m_virtualKeyCode[m_uTailer].bKeyPress) {
                        m_uKbdStatus = m_uKbdStatus | KBD_CTRL | KBD_RCTRL;
                    }
                    else {
                        m_uKbdStatus &= ~KBD_RCTRL;
                        if (!(m_uKbdStatus & KBD_LCTRL)) {
                            m_uKbdStatus &= ~KBD_CTRL;
                        }
                    }

                    break;
                }
                case 0x38:{ // Right ALT
                    if (m_virtualKeyCode[m_uTailer].bKeyPress) {
                        m_uKbdStatus = m_uKbdStatus | KBD_ALT | KBD_RALT;
                    }
                    else {
                        m_uKbdStatus &= ~KBD_RALT;
                        if (!(m_uKbdStatus & KBD_LALT)) {
                            m_uKbdStatus &= ~KBD_ALT;
                        }
                    }

                    break;
                }
            }
        }
    }
    else {
        m_virtualKeyCode[m_uTailer].bKeyPress = ((c & 0x80) == 0x00);

        c &= 0x7F;

        // When the Num lock switch
        if (c >= 0x47 && c <= 0x53 && !(m_uLedStatus & 0x0002)) {
            c -= 0x47;
            m_virtualKeyCode[m_uTailer].uVirKey = s_puKeymapEspecial[c];
        }
        else {
            m_virtualKeyCode[m_uTailer].uVirKey = s_puKeymapSignal[c];
        }

        if (c > 0x58) {
            m_virtualKeyCode[m_uTailer].uVirKey = VK_NOSUPPORT;
        }

        switch (c) {
            case 0x1D:{ // Left CTRL
                if (m_virtualKeyCode[m_uTailer].bKeyPress) {
                    m_uKbdStatus = m_uKbdStatus | KBD_CTRL | KBD_LCTRL;
                }
                else {
                    m_uKbdStatus &= ~KBD_LCTRL;
                    if (!(m_uKbdStatus & KBD_RCTRL)) {
                        m_uKbdStatus &= ~KBD_CTRL;
                    }
                }

                break;
            }
            case 0x38:{ // Left ALT
                if (m_virtualKeyCode[m_uTailer].bKeyPress) {
                    m_uKbdStatus = m_uKbdStatus | KBD_ALT | KBD_LALT;
                }
                else {
                    m_uKbdStatus &= ~KBD_LALT;
                    if (!(m_uKbdStatus & KBD_RALT)) {
                        m_uKbdStatus &= ~KBD_ALT;
                    }
                }

                break;
            }
            case 0x2A:{ // Left SHIFT
                if (m_virtualKeyCode[m_uTailer].bKeyPress) {
//                    PrintComPort(" Left shift press ");
                    m_uKbdStatus = m_uKbdStatus | KBD_SHIFT | KBD_LSHIFT;
                }
                else {
//                    PrintComPort(" Left shift release ");
                    m_uKbdStatus &= ~KBD_LSHIFT;
                    if (!(m_uKbdStatus & KBD_RSHIFT)) {
                        m_uKbdStatus &= ~KBD_SHIFT;
                    }
                }

                break;
            }
            case 0x36:{ // Right SHIFT
                if (m_virtualKeyCode[m_uTailer].bKeyPress) {
                    m_uKbdStatus = m_uKbdStatus | KBD_SHIFT | KBD_RSHIFT;
                }
                else {
                    m_uKbdStatus &= ~KBD_RSHIFT;
                    if (!(m_uKbdStatus & KBD_LSHIFT)) {
                        m_uKbdStatus &= ~KBD_SHIFT;
                    }
                }

                break;
            }
        }
    }

    if (0xE0 == s_uPreScancode) {
        m_virtualKeyCode[m_uTailer].uFlags = KEYEVENTFLAG__SPECIAL_0;
    }
    else if (0xE1 == s_uPreScancode) {
        m_virtualKeyCode[m_uTailer].uFlags = KEYEVENTFLAG__SPECIAL_1;
    }
    else if (m_virtualKeyCode[m_uTailer].bKeyPress) {
        m_virtualKeyCode[m_uTailer].uFlags = KEYEVENTFLAG_KEYDOWN;
    }
    else {
        m_virtualKeyCode[m_uTailer].uFlags = KEYEVENTFLAG_KEYUP;
    }

    m_virtualKeyCode[m_uTailer].uScanCode = c;
    m_virtualKeyCode[m_uTailer].uKbdStatus = m_uKbdStatus;
    m_virtualKeyCode[m_uTailer].uLedStatus = m_uLedStatus;
    s_uPreScancode = INITVALUE;

    if (((m_uTailer + 1) % KBDBUF) == m_uHeader) {
        // The buffer full
        return 1;
    }

    m_uTailer++;

    if (KBDBUF == m_uTailer) {
        m_uTailer = 0;
    }

    return 0;
}

ECode Keyboard::Read(
    /* [in] */ UInt64 u64Offset,
    /* [in] */ UInt32 bytesToRead,
    /* [out] */ MemoryBuf * pBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    // in fact, the buffer GetLength equal (KBDBUF - 1)
    if (pBuffer == NULL || bytesToRead >= KBDBUF
        || (uint32_t)pBuffer->GetCapacity()
        < bytesToRead * sizeof(KBDStatus)
        || (ppCompletionEvent && *ppCompletionEvent)
        || (u64Offset != 0)) {
        return E_INVALID_ARGUMENT;
    }

    if (0 == bytesToRead) {
        return NOERROR;
    }

    while (((KBDBUF + m_uTailer - m_uHeader) % KBDBUF)
        < bytesToRead) {
        m_event.Wait(NULL, NULL);
    }

    if (bytesToRead + m_uHeader <= KBDBUF) {
        pBuffer->Copy((Byte *)&m_virtualKeyCode[m_uHeader],
                        bytesToRead * sizeof(KBDStatus));

        m_uHeader += bytesToRead;
        Assert(m_uHeader <= KBDBUF);
        if (KBDBUF == m_uHeader) {
            m_uHeader = 0;
        }
    }
    // Tailer < Header
    else {
        Assert(m_uTailer < m_uHeader);
        uint32_t uTowPartRead;

        uint8_t uLeaveSize = KBDBUF - m_uHeader;
        pBuffer->Copy((Byte *)&m_virtualKeyCode[m_uHeader],
                                        uLeaveSize * sizeof(KBDStatus));
        uTowPartRead = bytesToRead - uLeaveSize;
        Assert(uTowPartRead > 0);

        pBuffer->Append((Byte *)m_virtualKeyCode,
                        uTowPartRead * sizeof(KBDStatus));
        m_uHeader = uTowPartRead;
    }

    return NOERROR;
}

ECode Keyboard::Write(
    /* [in] */ UInt64 u64Offset,
    /* [in] */ const MemoryBuf & buffer,
    /* [out] */ UInt32 * pBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode Keyboard::Control(
    /* [in] */ Handle32 nControlCode,
    /* [in] */ const MemoryBuf & inBuffer,
    /* [out] */ MemoryBuf * pOutBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    if ((nControlCode < 0) || (nControlCode > 4)
        || (ppCompletionEvent && *ppCompletionEvent)) {
        return E_INVALID_ARGUMENT;
    }

    ECode ec = NOERROR;

    switch (nControlCode) {
        case 0:{
            if (pOutBuffer == NULL || !pOutBuffer->GetCapacity()) {
                return E_INVALID_ARGUMENT;
            }
            else {
                ec = SetTypematicRate(*(uint_t *)inBuffer.GetPayload());
            }

            break;
        }

        case 1:{
            uint_t uRate = 0;

            if (pOutBuffer == NULL || inBuffer.IsNullOrEmpty()) {
                return E_INVALID_ARGUMENT;
            }
            else {
                ec = GetTypematicRate(&uRate);
                pOutBuffer->Copy((Byte *)&uRate, 4);
            }

            break;
        }

        case 2:{
            if (pOutBuffer == NULL || !pOutBuffer->GetCapacity()) {
                return E_INVALID_ARGUMENT;
            }
            else {
                ec = SetTypematicDelay(*(uint_t *)inBuffer.GetPayload());
            }

            break;
        }

        case 3:{
            uint_t uDelay = 0;

            if (pOutBuffer == NULL || inBuffer.IsNullOrEmpty()) {
                return E_INVALID_ARGUMENT;
            }
            else {
                ec = GetTypematicDelay(&uDelay);
                pOutBuffer->Copy((Byte *)&uDelay, 4);
            }

            break;
        }
        case 4:{
            if (pOutBuffer == NULL || !pOutBuffer->GetCapacity()
                || inBuffer.IsNullOrEmpty()) {
                return E_INVALID_ARGUMENT;
            }
            else {
                uint32_t    uCurrent = m_uHeader;

                while (uCurrent != m_uTailer) {
                    if (m_virtualKeyCode[uCurrent].bKeyPress) {
                        return NOERROR;
                    }

                    uCurrent = (uCurrent + 1) % KBDBUF;
                }

                ec = S_FALSE;
            }

            break;
        }
    }

    return ec;
}

ECode Keyboard::Initialize(KBDInfo *pConfigInfo)
{
    uint8_t uData = 0;

    m_uPortBase = pConfigInfo->uBasePort;
    m_uIrq = pConfigInfo->uIrq;

    // Receive any other data.
    while (KbdHasData(m_uPortBase)) {
        KRecvData(m_uPortBase, &uData);
    }

    // i8042 self test
    KSendCommand(m_uPortBase, KBD_CCMD_SELF_TEST);
    KRecvData(m_uPortBase, &uData);
    if (0x55 != uData) {
        kprintf("The i8042 self-test failed, return = 0x%02x\n", uData);
        return E_FAIL;
    }

    // keyboard interface test
    KSendCommand(m_uPortBase, KBD_CCMD_KBD_TEST);
    KRecvData(m_uPortBase, &uData);
    if (0x00 != uData) {
        kprintf("The keyboard interface test failed, return = 0x%02x\n",
                        uData);
        return E_FAIL;
    }

    // Enable keyboard interface
    KSendCommand(m_uPortBase, KBD_CCMD_KBD_ENABLE);

    // Reset keyboard
    do {
        KSendData(m_uPortBase, KBD_CMD_RESET);
        KRecvData(m_uPortBase, &uData);
        if (KBD_REPLY_ACK == uData) {
            break;
        }
        if (KBD_REPLY_RESEND == uData) {
            kprintf("The keyboard self-test failed, no ack\n");
            return E_FAIL;
        }
    } while (1);

    KRecvData(m_uPortBase, &uData);
    if (KBD_REPLY_POR != uData) {
        kprintf("The keyboard self-test failed, no pro\n");
        return E_FAIL;
    }

    // Set keyboard controller mode, so no scan code.
    if (KbdSendData(m_uPortBase, KBD_CMD_DISABLE)) {
        kprintf("Disable keyboard, no ack\n");
        return E_FAIL;
    }

    KSendCommand(m_uPortBase, KBD_CCMD_WRITE_MODE);
    if (KSendData(m_uPortBase, KBD_MODE_KBD_INT
        | KBD_MODE_SYS
        | KBD_MODE_DISABLE_MOUSE
        | KBD_MODE_KCC)) {
        kprintf("Config keyboard failed\n");
        return E_FAIL;
    }

    KSendCommand(m_uPortBase, KBD_CCMD_READ_MODE);
    KRecvData(m_uPortBase, &uData);
    if (!(uData & KBD_MODE_KCC)) { // If it isn't support translate auto.
        // set scan code set 1
        if (KbdSendComandAndArgument(m_uPortBase, KBD_CMD_CODE_SET, 0x01)) {
            kprintf("Can not set scan code set 1\n");
            return E_FAIL;
        }
    }

    // Set typematic rate and delay. now, It's fastest.
    if (KbdSendComandAndArgument(m_uPortBase, KBD_CMD_SET_RATE, 0x00)) {
        kprintf("Set typematic failed.\n");
    }

    // Set Led
    if (KbdSendComandAndArgument(
            m_uPortBase, KBD_CMD_SET_LEDS, (uint8_t)m_uLedStatus)) {
        kprintf("Set led failed.\n");
    }

    // Re-Enable keyboard
    if (KbdSendData(m_uPortBase, KBD_CMD_ENABLE)) {
        kprintf("Keyboard re-enable no ack.\n");
        return E_FAIL;
    }

    uint32_t uFlags = SaveFlagsAndCli();
    // Register the ISR.
    AddIt8172Device(m_uIrq, &KeyboardIsr, this);

    RestoreIF(uFlags);

    return NOERROR;
}

ECode Keyboard::SetTypematicRate(
    /* [in] */ uint_t uTypematicRate)
{
    if (uTypematicRate < 0 || uTypematicRate > 31) {
        return E_INVALID_ARGUMENT;
    }

    s_uLastCommand = KBD_CMD_SET_RATE;
    s_uLastArgument = (m_uTypematic & 0xe0) | uTypematicRate;

    if (KbdSendData(m_uPortBase, s_uLastCommand)) {
        s_uLastException = COMMANDEXCEPTION;
        return NOERROR;
    }

    if (KbdSendData(m_uPortBase, s_uLastArgument)) {
        s_uLastException = ARGUMENTEXCEPTION;
        return NOERROR;
    }

    m_uTypematic = s_uLastArgument;
    s_uLastCommand = INITVALUE;
    s_uLastArgument = INITVALUE;

    return NOERROR;
}

ECode Keyboard::GetTypematicRate(
    /* [out] */ uint_t *puTypematicRate)
{
    Assert(m_uTypematic < (2^7));
    *puTypematicRate = m_uTypematic & 0x1F;
    Assert(*puTypematicRate < (2^5));

    return NOERROR;
}

ECode Keyboard::SetTypematicDelay(
    /* [in] */ uint_t uTypematicDelay)
{
    if (uTypematicDelay < 0 || uTypematicDelay > 3) {
        return E_INVALID_ARGUMENT;
    }

    s_uLastCommand = KBD_CMD_SET_RATE;
    s_uLastArgument = (m_uTypematic & 0x1f) | (uTypematicDelay << 5);

    if (KbdSendData(m_uPortBase, s_uLastCommand)) {
        s_uLastException = COMMANDEXCEPTION;
        return NOERROR;
    }

    if (KbdSendData(m_uPortBase, s_uLastArgument)) {
        s_uLastException = ARGUMENTEXCEPTION;
        return NOERROR;
    }

    m_uTypematic = s_uLastArgument;
    s_uLastCommand = INITVALUE;
    s_uLastArgument = INITVALUE;

    return NOERROR;
}

ECode Keyboard::GetTypematicDelay(
    /* [out] */ uint_t *puTypematicDelay)
{
    Assert(m_uTypematic < (2^7));
    *puTypematicDelay = m_uTypematic >> 5;
    Assert(*puTypematicDelay < (2^2));

    return NOERROR;
}
